package com.globefrog.framework.log;
import com.google.common.collect.Maps;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;

/**
 * 日志信息过滤器.
 *
 * @author caijinbang
 * @date 2020-11-07 23:57
 */
@Slf4j
public abstract class LogMessageFilter {

  private final ConcurrentMap<String, Pattern> patternCache = Maps.newConcurrentMap();

  protected String mask(String message, String patternExpression) {
    Pattern pattern;
    try {
      pattern = this.getPattern(patternExpression);
    } catch (Exception e) {
      log.error("failed to parse pattern, pattern={}", patternExpression, e);
      return "failed to parse pattern, entire message masked, pattern=" + patternExpression;
    }
    Matcher matcher = pattern.matcher(message);
    StringBuilder builder = this.filterMessage(message, matcher);
    return builder.length() > 0 ? builder.toString() : this.maskEntireMessage(patternExpression);
  }

  private StringBuilder filterMessage(String message, Matcher matcher) {
    StringBuilder builder = new StringBuilder();
    int current = 0;
    while (matcher.find()) {
      int groupCount = matcher.groupCount();
      for (int i = 1; i <= groupCount; ++i) {
        int start = matcher.start(i);
        int end = matcher.end(i);
        builder.append(message.subSequence(current, start)).append("--masked--");
        current = end;
      }
    }

    if (current > 0 && current < message.length()) {
      builder.append(message.subSequence(current, message.length()));
    }

    return builder;
  }

  private Pattern getPattern(String patternExpression) {
    Pattern pattern = this.patternCache.get(patternExpression);
    if (pattern == null) {
      pattern = Pattern.compile(patternExpression);
      Pattern existPattern = this.patternCache.putIfAbsent(patternExpression, pattern);
      if (existPattern != null) {
        log.warn("patternExpression:{} has exists.", patternExpression);
      }
    }
    return pattern;
  }

  private String maskEntireMessage(String pattern) {
    return "pattern or group not found, entire message masked, pattern=" + pattern;
  }

  public abstract String filter(String className, String message);
}
